package com.cci.zkweb.core.util;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import com.astersoft.lib.cypher.ASCrypt;
import com.cci.lib.ext.config.SharedConfig;
import com.cci.zkweb.core.entity.User;

public class LdapUtils {
	
	/**
	 * Check user authentication
	 * @param username
	 * @param password
	 * @return null if authentication fail
	 */
	public static User authenticate(String username, String password) {
	    try {
	        User user = getUserInfo(username);
	        
	        Properties props = getBaseLdapProperties();
	        props.put(Context.SECURITY_PRINCIPAL, user.getDn());
	        props.put(Context.SECURITY_CREDENTIALS, password);

	        new InitialDirContext(props);
	        
	        user.setAccount(username);
	        return user;
	    }
	    catch (NamingException e) {
	    	e.printStackTrace();
	        return null;
	    }
	}
	
	/**
	 * Get user info from his login name (based on login filter attribute)
	 * @param username
	 * @return
	 * @throws NamingException
	 */
	public static User getUserInfo(String username) 
	throws NamingException {
		InitialDirContext context = new InitialDirContext(getSystemAuthProperties());
		User user = new User();
		
		try {
		    SearchControls ctrls = new SearchControls();
		    ctrls.setReturningAttributes(new String[] { 
		    									"cn",
		    									"givenName", 
		    									"mail",
		    									"userPrincipalName",
		    									"sn" });
		    ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
	
		    NamingEnumeration<SearchResult> answers = context.search(
		    											SharedConfig.getInstance().getValue("ldap", "basedn"), 
		    											"(cn=" + username + ")", ctrls);
		    SearchResult result = answers.next();
		    
		    user.setFullName(secureGetAttribute(result.getAttributes(), "sn", "") + " " + secureGetAttribute(result.getAttributes(), "givenName", ""));
		    user.setEmail(secureGetAttribute(result.getAttributes(), "mail") == null ? secureGetAttribute(result.getAttributes(), "userPrincipalName", "") : secureGetAttribute(result.getAttributes(), "mail", ""));
		    user.setDn(result.getNameInNamespace());
		    user.setCn(secureGetAttribute(result.getAttributes(), "cn", ""));
		} finally {
			context.close();
		}
	    
	    return user;
	}
	
	/**
	 * Get the base LDAP properties
	 * @return
	 */
	private static Properties getBaseLdapProperties() {
		Properties props = new Properties();
	    props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
	    props.put(Context.SECURITY_AUTHENTICATION, "simple");
	    props.put(Context.PROVIDER_URL, "ldap://" + SharedConfig.getInstance().getValue("ldap", "server") + ":" + SharedConfig.getInstance().getIntegerValue("ldap", "port"));
	    props.put(Context.REFERRAL, "ignore");
	    
	    return props;
	}
	
	/**
	 * Get LDAP properties with system authentication
	 * @return
	 */
	private static Properties getSystemAuthProperties() {
		Properties props = getBaseLdapProperties();
		props.put(Context.SECURITY_PRINCIPAL, SharedConfig.getInstance().getValue("ldap", "user"));
        props.put(Context.SECURITY_CREDENTIALS, ASCrypt.decrypt(SharedConfig.getInstance().getValue("ldap", "password")));
		
		return props;
	}
	
	/**
	 * Get attribute from list in a secure way
	 * 
	 * @param attributeList
	 * @param attributeName
	 * @return
	 */
	private static String secureGetAttribute(Attributes attributeList, String attributeName) 
	throws NamingException {
		return secureGetAttribute(attributeList, attributeName, null);
	}
	private static String secureGetAttribute(Attributes attributeList, String attributeName, String defaultValue) 
	throws NamingException {
		if(attributeList == null) return defaultValue;
		if(attributeList.get(attributeName) != null) return (String)attributeList.get(attributeName).get();
		return defaultValue;
	}
}
